package cn.gong.json.utils;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import org.json.JSONArray;
import org.json.JSONObject;

import cn.gong.json.annotation.ClassType;
import cn.gong.json.annotation.NowJson;
import cn.gong.json.annotation.OldJson;
import cn.gong.utils.LogUtils;
import cn.gong.utils.StringUtils;

public class BeanUtils {
	public static final String CLASSNAME_STRING = "java.lang.String";
	public static final String CLASSNAME_INTEGER = "java.lang.Integer";
	public static final String CLASSNAME_INT = "int";
	public static final String CLASSNAME_LONG = "long";
	public static final String CLASSNAME_BYTE = "byte";
	public static final String CLASSNAME_CHAR = "char";
	public static final String CLASSNAME_DOUBLE = "double";
	public static final String CLASSNAME_FLOAT = "float";
	public static final String CLASSNAME_BOOLEAN = "boolean";
	public static final String CLASSNAME_ARRAY = "java.util.ArrayList";

	public static <T> ArrayList<T> getNowBeanArray(Class<T> clazz, String json) {
		try {
			return getNowBeanArray(clazz, new JSONArray(json));
		} catch (Exception e) {
			LogUtils.exception(e);
			return null;
		}
	}

	public static <T> ArrayList<T> getNowBeanArray(Class<T> clazz, JSONArray ja) {
		ArrayList<T> array = new ArrayList<T>();
		for (int i = 0; i < ja.length(); i++) {
			try {
				array.add(BeanUtils.nowBean(clazz, ja.getJSONObject(i)));
			} catch (Exception e) {
				LogUtils.exception(e);
			}
		}
		return array;
	}

	public static <T> ArrayList<T> getOldBeanArray(Class<T> clazz, String json) {
		try {
			return getOldBeanArray(clazz, new JSONArray(json));
		} catch (Exception e) {
			LogUtils.exception(e);
			return null;
		}
	}

	public static <T> ArrayList<T> getOldBeanArray(Class<T> clazz, JSONArray ja) {
		ArrayList<T> array = new ArrayList<T>();
		for (int i = 0; i < ja.length(); i++) {
			try {
				array.add(BeanUtils.oldBean(clazz, ja.getJSONObject(i)));
			} catch (Exception e) {
				LogUtils.exception(e);
			}
		}
		return array;
	}

	public static <T> T nowBean(Class<T> clazz, String json) {
		try {
			return nowBean(clazz, new JSONObject(json));
		} catch (Exception e) {
			LogUtils.exception(e);
			return null;
		}
	}

	public static <T> T nowBean(Class<T> clazz, JSONObject jsonObject) {
		return jsonObjectToBean(clazz, jsonObject, true);
	}

	public static <T> T oldBean(Class<T> clazz, String json) {
		try {
			return oldBean(clazz, new JSONObject(json));
		} catch (Exception e) {
			LogUtils.exception(e);
			return null;
		}
	}

	public static <T> T oldBean(Class<T> clazz, JSONObject jsonObject) {
		return jsonObjectToBean(clazz, jsonObject, false);
	}

	private static <T> T jsonObjectToBean(Class<T> clazz, JSONObject jsonObject, boolean isNow) {
		T newInstance = null;
		try {
			newInstance = clazz.newInstance();
		} catch (Exception e) {
			LogUtils.exception(e);
		}

		HashMap<String, String> names = new HashMap<String, String>();
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			if (isNow) {
				NowJson annotation = field.getAnnotation(NowJson.class);
				if (annotation != null) {
					names.put(annotation.value(), field.getName());
				}
			} else {
				OldJson annotation = field.getAnnotation(OldJson.class);
				if (annotation != null) {
					names.put(annotation.value(), field.getName());
				}
			}
		}

		Iterator keys = jsonObject.keys();
		Field name = null;
		while (keys.hasNext()) {
			try {
				String key = (String) keys.next();
				String filedName = names.get(key);
				name = clazz.getDeclaredField(filedName == null ? key : filedName);
				name.setAccessible(true);
				String className = name.getType().getName().trim();
				if (CLASSNAME_STRING.equals(className)) {
					name.set(newInstance, jsonObject.getString(key));
				} else if (CLASSNAME_INT.equals(className)) {
					name.setInt(newInstance, jsonObject.getInt(key));
				} else if (CLASSNAME_LONG.equals(className)) {
					name.setLong(newInstance, jsonObject.getLong(key));
				} else if (CLASSNAME_BOOLEAN.equals(className)) {
					name.setBoolean(newInstance, jsonObject.getBoolean(key));
				} else if (CLASSNAME_ARRAY.equals(className)) {
					String genericsTypeName = name.getAnnotation(ClassType.class).classType();
					JSONArray jsonArray = jsonObject.getJSONArray(key);
					ArrayList array = new ArrayList();
					if (isBaseDataType(genericsTypeName)) {
						for (int i = 0; i < jsonArray.length(); i++) {
							array.add(jsonArray.get(i));
						}
					} else {
						for (int i = 0; i < jsonArray.length(); i++) {
							array.add(jsonObjectToBean(Class.forName(genericsTypeName), jsonArray.getJSONObject(i), isNow));
						}
					}
					name.set(newInstance, array);
				} else {
					if (StringUtils.isNotBlank(jsonObject.get(key) + "")) {
						name.set(newInstance, jsonObjectToBean(Class.forName(className), jsonObject.getJSONObject(key), isNow));
					}
				}
			} catch (Exception e) {
				LogUtils.log("name:" + (name == null ? "" : name.getName()), BeanUtils.class);
				LogUtils.exception(e);
			}
		}
		return newInstance;
	}

	public static String nowJsonArray(ArrayList list) {
		if (list != null && list.size() > 0) {
			JSONArray ja = new JSONArray();
			for (int i = 0; i < list.size(); i++) {
				ja.put(nowJsonObject(list.get(i)));
			}
			return ja.toString();
		} else {
			return "";
		}
	}

	public static String nowJson(Object obj) {
		return nowJsonObject(obj).toString();
	}

	public static JSONObject nowJsonObject(Object obj) {
		LogUtils.log("obj==null:" + (obj == null), BeanUtils.class);
		if (obj == null) {
			return null;
		}
		Class clazz = obj.getClass();
		JSONObject jo = new JSONObject();

		Field[] fields = clazz.getDeclaredFields();
		for (int i = 0; i < fields.length; i++) {
			try {

				Field field = fields[i];
				field.setAccessible(true);
				String className = field.getType().getName().trim();
				NowJson annotation = field.getAnnotation(NowJson.class);
				if (CLASSNAME_STRING.equals(className)) {
					if (annotation != null) {
						jo.put(annotation.value(), field.get(obj));
					} else {
						jo.put(field.getName(), field.get(obj));
					}
				} else if (CLASSNAME_INT.equals(className)) {
					if (annotation != null) {
						jo.put(annotation.value(), field.getInt(obj));
					} else {
						jo.put(field.getName(), field.getInt(obj));
					}
				} else if (CLASSNAME_LONG.equals(className)) {
					if (annotation != null) {
						jo.put(annotation.value(), field.getLong(obj));
					} else {
						jo.put(field.getName(), field.getLong(obj));
					}
				} else if (CLASSNAME_BOOLEAN.equals(className)) {
					if (annotation != null) {
						jo.put(annotation.value(), field.getBoolean(obj));
					} else {
						jo.put(field.getName(), field.getBoolean(obj));
					}
				} else if (CLASSNAME_ARRAY.equals(className)) {
					String genericsTypeName = field.getAnnotation(ClassType.class).classType();
					JSONArray ja = new JSONArray();
					ArrayList array = (ArrayList) field.get(obj);

					if (isBaseDataType(genericsTypeName)) {
						for (int j = 0; j < array.size(); j++) {
							ja.put(array.get(j));
						}
					} else {
						for (int j = 0; j < array.size(); j++) {
							// ja.put(nowJson(array.get(j)));
							ja.put(nowJsonObject(array.get(j)));
						}
					}

					if (annotation != null) {
						jo.put(annotation.value(), ja);
					} else {
						jo.put(field.getName(), ja);
					}
				} else {
					if (annotation != null) {
						// jo.put(annotation.value(), nowJson(field.get(obj)));
						jo.put(annotation.value(), nowJsonObject(field.get(obj)));
					} else {
						jo.put(field.getName(), nowJsonObject(field.get(obj)));
						// jo.put(field.getName(), nowJson(field.get(obj)));
					}
				}
			} catch (Exception e) {
				LogUtils.exception(e);
			}
		}
		return jo;
	}

	private static boolean isBaseDataType(String dataType) {
		if (CLASSNAME_BOOLEAN.equals(dataType)) {
			return true;
		} else if (CLASSNAME_INT.equals(dataType)) {
			return true;
		} else if (CLASSNAME_LONG.equals(dataType)) {
			return true;
		} else if (CLASSNAME_STRING.equals(dataType)) {
			return true;
		} else if (CLASSNAME_BYTE.equals(dataType)) {
			return true;
		} else if (CLASSNAME_CHAR.equals(dataType)) {
			return true;
		} else if (CLASSNAME_DOUBLE.equals(dataType)) {
			return true;
		} else if (CLASSNAME_FLOAT.equals(dataType)) {
			return true;
		} else if (CLASSNAME_INTEGER.equals(dataType)) {
			return true;
		} else {
			return false;
		}
	}
}
